Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create safer isValidAddress method #11089

Merged
merged 6 commits into from
May 17, 2021
Merged

Conversation

brad-decker
Copy link
Contributor

Instead of using the isValidAddress method from ethereumjs-util that now throws when a non hex prefixed string is provided, or the overloaded isValidAddress helper we maintain that checks a number of things, we should use a more effective shared method.

This PR creates two new shared methods:

  1. isBurnAddress - is the address exactly 0x0000000000000000000000000000000000000000 which is an address that is used sometimes for burning tokens and one we want to guard our users from using.
  2. isValidHexAddress - checks if the address is possibly a checksum address and uses the checksum address validation method or the address validation method appropriately.

The tests from our 'isValidAddress' implementation have been copied over and only ONE has been altered fundamentally. This new method does not return true for forty-character non hex-prefixed strings. If user input would be valid without the hex prefix, then it should be hex prefixed before calling this method.

@brad-decker brad-decker requested a review from a team as a code owner May 13, 2021 19:04
@brad-decker brad-decker requested a review from danjm May 13, 2021 19:04
@metamaskbot
Copy link
Collaborator

Builds ready [88fdb55]
Page Load Metrics (569 ± 51 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint45776294
domContentLoaded38568756710751
load38968956910751
domInteractive38568756710751

@metamaskbot
Copy link
Collaborator

Builds ready [bf90736]
Page Load Metrics (575 ± 33 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint44735984
domContentLoaded3846315737033
load3866325757033
domInteractive3846315737033

jsconfig.json Outdated
"node_modules",
"patches",
"test-artifacts"
]
Copy link
Contributor

@ryanml ryanml May 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

were these exclusions no longer needed here or?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, not part of this i'll remove. With them in autocomplete just randomly starts failing for me.

ryanml
ryanml previously approved these changes May 13, 2021
Copy link
Contributor

@ryanml ryanml left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@metamaskbot
Copy link
Collaborator

Builds ready [b4c496d]
Page Load Metrics (602 ± 33 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint448663105
domContentLoaded4076926016933
load4086946026933
domInteractive4076926006933

@brad-decker
Copy link
Contributor Author

brad-decker commented May 14, 2021

I discovered that our internal 'isHex' tool also doesn't validate that the hex string starts with '0x' so the previous fix by @tmashuang (#11071) was not sufficient to prevent errors for real addresses that aren't hex prefixed, so this PR replaces the usages of that with isValidHexAddress (in no case where it was used did we expect anything other than an address. Note that Thomaas' change does prevent issues when non hex strings (like domain names) were sent to the icons.

Also reverted the change that made non 0x prefixed hex strings invalid for the purposes of this method but we should explore changing this behavior in the future.

@brad-decker
Copy link
Contributor Author

One more tweak, i went through all usages of isValidAddress (both our util and the ethereumjs-util function) and restored it's rightful behavior by adding a new prop to the new helper called 'allowNonPrefixed' ...why?

ethereumjs-util.isValidAddress prior to my update would return false for non hex prefixed strings, whereas our method would return true provided that it was a 40 character hex string.

This new prop allows us to have both behaviors in one method, and a path for gradual migration of using non hex prefixed values and allowing them from users.

@metamaskbot
Copy link
Collaborator

Builds ready [3519fb3]
Page Load Metrics (640 ± 62 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint47746294
domContentLoaded39584763712962
load39784864012962
domInteractive39584663712962

Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the new function, and the consolidation of all of those utility functions. But it seems that there are a lot of potentially dapp-breaking changes in here still, and a few undesirable changes to the UI.

It makes perfect sense to me that we'd ensure our dapp interactions use properly-formatted addresses, but when making any non-backwards-compatible changes to our provider (however reasonable) we should consider how best to communicate these changes before making them. Giving plenty of advanced notice, and maybe shipping a deprecation notice ahead of time, would be much friendlier to devs. Especially in cases like this where there are few downsides for us to preserving this behaviour a little longer.

And regarding checksumming, we should consider adopting support for EIP-1191, and considering addresses checksummed by EIP-1191 or by EIP-55 as valid. Or alternatively, we could skip validating mixed-case addresses as checksummed for now (or not extending our validation further at least). I'd hate to see us accidentally stand in the way of a new, helpful EIP becoming more widespread.

app/scripts/controllers/preferences.js Outdated Show resolved Hide resolved
app/scripts/controllers/transactions/lib/util.js Outdated Show resolved Hide resolved
app/scripts/lib/typed-message-manager.js Outdated Show resolved Hide resolved
shared/modules/hexstring-utils.js Outdated Show resolved Hide resolved
ui/components/ui/identicon/identicon.component.js Outdated Show resolved Hide resolved
@@ -18,7 +19,7 @@ function IconFactory(jazzicon) {
IconFactory.prototype.iconForAddress = function (address, diameter) {
let addr = address;

if (isHex(address)) {
if (isValidHexAddress(address, false)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems dangerous. Previously checksumAddress would add the prefix, and checksum the address regardless of whether it's mixed-case or not. Now it requires the address is already hex-prefixed and either checksummed already or all one case. This means for any other case, we're skipping this normalization step, and we'll be generating a different icon than before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

under the hood, the utility method 'checksumAddress' calls ethereumjs-util's toChecksumAddress which in 5.1 adds the '0x' prefix, and in the new version, it throws an error. It seems like we should probably add a new utility method that mimics this behavior because as is even when allowing this to validate true for a non hex prefixed string, the string is not modified meaning it would immediately throw on the next line.

I'm going to add this utility method now, and replace usages of the old helper method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually I should do that in a new PR instead to prevent convoluting this with new logic

@@ -167,7 +165,7 @@ class AddToken extends Component {
autoFilled: false,
});

const addressIsValid = isValidAddress(customAddress);
const addressIsValid = isValidHexAddress(customAddress, false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior is pre-existing, but it does seem strange to me that we'd show an "invalid address" error when the address is mixed-case but doesn't match our checksum. Particularly when we're not checksumming the addresses properly according to EIP-1191, as we're omitting the chainID. As a result, we'd be showing "invalid address" when an address was properly checksummed in accordance with EIP-1191.

@@ -101,7 +104,7 @@ export default class AddRecipient extends Component {

let content;

if (isValidAddress(query)) {
if (!isBurnAddress(query) && isValidHexAddress(query)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems overly harsh to disallow differently-checksummed addresses here.

The same goes for the other uses of this function in the Send flow and the contacts flow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While that is true, it is the previously implemented behavior. Should I change that as part of this PR? These places used the previous 'isValidAddress' custom function which did check for mixed case and do validation using isValidChecksum.

Copy link
Member

@Gudahtt Gudahtt May 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be perfectly reasonable to stick with targeting pre-v9.5.0 behavior IMO, and leave this behaving as it did before. Particularly since this is destined for a hotfix.

ui/pages/settings/settings.container.js Outdated Show resolved Hide resolved
ui/pages/swaps/swaps.util.js Outdated Show resolved Hide resolved
@metamaskbot
Copy link
Collaborator

Builds ready [f0140d6]
Page Load Metrics (732 ± 52 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint54836774
domContentLoaded45784573010952
load45884673210952
domInteractive45784572910952

@brad-decker
Copy link
Contributor Author

@Gudahtt my goal was to preserve the behavior pre 9.5.0 and the ethereumjs-util and I thought I was being diligent in doing so. 😓 I'm going to add a new parameter (validateChecksumAddress) that will default to false and be opt-in. I hope you won't mind being a second pair of eyes again while I do another pass :(

@Gudahtt
Copy link
Member

Gudahtt commented May 17, 2021

Sure, of course! That solution sounds perfect to me.

@brad-decker
Copy link
Contributor Author

There are no futher usages of either:

  1. ethereumjs-util.isValidAddress
  2. isValidAddress from our helpers

in all cases to restore pre 9.5 behavior the new isValidHexAddress should be called in the following ways:
a. For 1 above should call isValidHexAddress(input, { allowNonPrefixed: false })
b. For 2 above should call isValidHexAddress(input, { mixedCaseUseChecksum: true }) and separately check if the burn address is used.

I did an audit of all places where isValidHexAddress is invoked (29 invocations)

  1. app/scripts/controllers/preferences.js - 1 invocation - previously used 1, correctly applied a.
  2. app/scripts/controllers/transactions/lib/util.js - 2 invocations - previously used 1, correctly applied a.
  3. app/scripts/lib/typed-message-manager.js - 1 invocation - previously used 1, correctly applied a.
  4. shared/modules/hexstring-utils.test.js - 8 invocations - new test file, irrelevant
  5. ui/pages/add-token/add-token.component.js - 1 invocation - previously used 1, correctly applied a.
  6. ui/pages/send/send.component.js - 1 invocation - previously used 1, correctly applied a.
  7. ui/pages/send/send-content/add-recipient/add-recipient.component.js - 1 invocation - previously used 2, correctly applied b.
  8. ui/pages/send/send-content/add-recipient/add-recipient.js - 1 invocation - previously used 2, correctly applied b.
  9. ui/pages/send/send-content/add-recipient/ens-input.component.js - 3 invocations - previously used 2, correctly applied b.
  10. ui/pages/settings/settings.container.js - 1 invocation - previously used 2, correctly applied b.
  11. ui/pages/settings/contact-list-tab/add-contact/add-contact.component.js - 1 invocation - previously used 2, correctly applied b.
  12. ui/pages/settings/contact-list-tab/edit-contact/edit-contact.component.js - 1 invocation - previously used 2, correctly applied b.
  13. ui/pages/swaps/swaps.util.js - 4 invocations - previously used 1, correctly applied a.

There are two files that deserve special mention:

  1. ui/helpers/utils/icon-factory.js - 2 invocations - previously only had 1 usage using 1, and so in that instance applied a.
  2. ui/components/ui/identicon/identicon.component.js - 1 invocation - previously did not have an invocation

the new usages of this utility method in the above two instances will be removed in a new PR adding the toChecksumHexAddress.

@ha842

This comment has been minimized.

@metamaskbot
Copy link
Collaborator

Builds ready [12b3631]
Page Load Metrics (592 ± 44 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint458462105
domContentLoaded3997005909144
load4017015929144
domInteractive3997005909144

@metamaskbot
Copy link
Collaborator

Builds ready [8dcd521]
Page Load Metrics (619 ± 53 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint479663115
domContentLoaded38081461711053
load38181661911053
domInteractive38081461711053

Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything looks good aside from these two identicon-related changes. I suggested that we revert changes for now so that we're not making things any worse, until we're prepared to fix those properly. But let me know if you think there's a good reason to keep these changes.

ui/helpers/utils/icon-factory.js Outdated Show resolved Hide resolved
ui/components/ui/identicon/identicon.component.js Outdated Show resolved Hide resolved
@metamaskbot
Copy link
Collaborator

Builds ready [d568288]
Page Load Metrics (636 ± 53 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint56776453
domContentLoaded40089163411153
load40289363611153
domInteractive40089163411153

Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@brad-decker
Copy link
Contributor Author

thank you thank you thank you, @Gudahtt. 🙏🏻 🎉 💯 🥇 🚀

@metamaskbot
Copy link
Collaborator

Builds ready [63861cb]
Page Load Metrics (611 ± 36 ms)
PlatformPageMetricMin (ms)Max (ms)Average (ms)StandardDeviation (ms)MarginOfError (ms)
ChromeHomefirstPaint45765695
domContentLoaded4066896097637
load4076906117636
domInteractive4066886097637

@brad-decker brad-decker merged commit 9386e3c into develop May 17, 2021
@brad-decker brad-decker deleted the use-safer-is-valid-address- branch May 17, 2021 19:01
@github-actions github-actions bot locked and limited conversation to collaborators May 17, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants